package ru.reaperoq.routing

import at.favre.lib.crypto.bcrypt.BCrypt
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.routing.*
import ru.reaperoq.database.Database
import ru.reaperoq.dto.AuthRequest
import ru.reaperoq.dto.UserResponse
import ru.reaperoq.dto.respondError
import ru.reaperoq.dto.respondSuccess
import ru.reaperoq.schema.UserRole
import ru.reaperoq.utils.getBody
import ru.reaperoq.utils.getUser
import ru.reaperoq.utils.withRole

fun Routing.usersRouting(onGenerateToken: (Int) -> String) {

    val userService = Database.userService
    post(URLS.Auth.REGISTER) {
        val body = call.getBody<AuthRequest>() ?: return@post
        try {
            val id = userService.create(body.username, body.password)
            val token = onGenerateToken(id)
            call.respondSuccess(token)
        } catch (_: Exception) {
            call.respondError("Пользователь уже существует", HttpStatusCode.Conflict)
        }
    }

    post(URLS.Auth.LOGIN) {
        val body = call.getBody<AuthRequest>() ?: return@post
        val user = userService.readByUsername(body.username) ?: run {
            call.respondError("Пользователь не найден", HttpStatusCode.NotFound)
            return@post
        }
        if (!BCrypt.verifyer().verify(body.password.toCharArray(), user.password).verified) {
            call.respondError("Неверный пароль", HttpStatusCode.Unauthorized)
            return@post
        }
        val token = onGenerateToken(user.id)
        call.respondSuccess(token)
    }

    authenticate {
        get(URLS.Users.ME) {
            val user = getUser() ?: return@get
            call.respondSuccess(UserResponse.from(user))
        }

        get(URLS.Users.USERS) {
            withRole(UserRole.ADMIN) {
                call.respondSuccess(userService.readAll().map { UserResponse.from(it) })
            }
        }

        get(URLS.Users.USER) {
            withRole(UserRole.ADMIN) {
                val id = call.parameters["id"]?.toIntOrNull() ?: run {
                    call.respondError("Неверный id", HttpStatusCode.BadRequest)
                    return@withRole
                }
                val user = userService.read(id) ?: run {
                    call.respondError("Пользователь не найден", HttpStatusCode.NotFound)
                    return@withRole
                }
                call.respondSuccess(UserResponse.from(user))
            }
        }

        patch(URLS.Users.USER) {
            withRole(UserRole.ADMIN) {
                val id = call.parameters["id"]?.toIntOrNull() ?: run {
                    call.respondError("Неверный id", HttpStatusCode.BadRequest)
                    return@withRole
                }
                val role = call.parameters["role"]?.uppercase()?.let(UserRole::valueOf)
                val locationId = call.parameters["locationId"]?.toIntOrNull()
                userService.update(id, role, locationId)
                call.respondSuccess(id)
            }
        }

        delete(URLS.Users.USER) {
            withRole(UserRole.ADMIN) {
                val id = call.parameters["id"]?.toIntOrNull() ?: run {
                    call.respondError("Неверный id", HttpStatusCode.BadRequest)
                    return@withRole
                }
                userService.delete(id)
                call.respondSuccess(id)
            }
        }
    }
}